package com.zmn.oms.third.yajule;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.zmn.common.utils.date.DateUtil;
import com.zmn.common.utils.string.StringUtil;
import com.zmn.oms.common.utils.HttpUtil;
import com.zmn.oms.third.yajule.dto.YajuleRequest;
import com.zmn.oms.third.yajule.dto.YajuleResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;

import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.Security;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;

/**
 * 雅居乐支付对接的工具实现类，包括签名生成，RSA解密
 *
 * @author xiewenbing
 * @date   2020/07/31
 */
@Slf4j
public class YajuleUtil {

    /**
     * RSA加密的最大长度
     */
    public static int MAX_RSA_ENC_LEN=117;

    /**
     * RSA解密的最大长度
     */
    public static int MAX_RSA_DEC_LEN=128;

    /**
     * RSA加解密模式、秘钥，向量
     */
    public static String RSA_MODEL="RSA/ECB/PKCS1Padding";

    /**
     * 请求数据进行md5签名
     * @param request
     */
    public static void makeSign(YajuleRequest request) {

        StringBuilder requestBuilder = new StringBuilder();
        requestBuilder.append("app_id=").append(request.getAppId()).append("&");
        requestBuilder.append("method=").append(request.getMethod()).append("&");
        requestBuilder.append("params=").append(request.getParams()).append("&");
        requestBuilder.append("timestamp=").append(request.getTimestamp()).append("&");
        requestBuilder.append("ver=").append(request.getVer()).append(YajuleConsts.APP_SECRET);

        String signSrc = requestBuilder.toString();
        String signDec = DigestUtils.md5Hex(signSrc);

        log.info("【{}】生成签名【{}】【{}】",YajuleConsts.CHANNEL_NAME,signSrc,signDec);

        request.setSign(signDec);
    }

    /**
     * 获取随机字符串
     * @return
     */
    public static String getRandomString(Long orderId) {

        Long timestamp = DateUtil.getNowTimestampMillis();
        return String.format("zmn-oms-%s-%s",orderId,timestamp);
    }

    /**
     * 得到公钥
     * @param publicKey 密钥字符串（经过base64编码）
     * @throws Exception
     */
    public static RSAPublicKey getPublicKey(String publicKey) {

        try {
            //通过X509编码的Key指令获得公钥对象
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKey));
            return (RSAPublicKey) keyFactory.generatePublic(x509KeySpec);
        } catch (Exception e) {
            log.info("YajuleUtil::getPublicKey failed...");
        }

        return null;
    }

    /**
     * rsa加密
     * @param content
     * @return
     */
    public static String rsaEncrypt(String content) {

        RSAPublicKey rsaPublicKey = getPublicKey(YajuleConsts.RSA_KEY);
        if (Objects.isNull(rsaPublicKey)) {
            return null;
        }

        // rsa 公钥加密
        try{
            Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance(RSA_MODEL);
            cipher.init(Cipher.ENCRYPT_MODE,rsaPublicKey);

            // 最大加密字节数，超出最大字节数需要分组加密
            byte[] inputBytes = content.getBytes();
            byte[] resultBytes = {};
            byte[] encBytes = {};
            int inputLength = inputBytes.length;
            int encOffset = 0;

            while (inputLength - encOffset > 0) {
                if (inputLength - encOffset > MAX_RSA_ENC_LEN) {
                    encBytes = cipher.doFinal(inputBytes, encOffset, MAX_RSA_ENC_LEN);
                    encOffset += MAX_RSA_ENC_LEN;
                } else {
                    encBytes = cipher.doFinal(inputBytes, encOffset, inputLength - encOffset);
                    encOffset = inputLength;
                }

                resultBytes = Arrays.copyOf(resultBytes, resultBytes.length + encBytes.length);
                System.arraycopy(encBytes, 0, resultBytes, resultBytes.length - encBytes.length, encBytes.length);
            }

            String encContent = java.util.Base64.getEncoder().encodeToString(resultBytes);
            return encContent.replaceAll("\r|\n","").replace("/","\\/");
        }catch (Exception e){
            e.printStackTrace();
        }

        return null;
    }

    /**
     * rsa解密
     * @param content
     * @return
     */
    public static String rsaDecrypt(String content) {

        RSAPublicKey rsaPublicKey = getPublicKey(YajuleConsts.RSA_KEY);
        if (Objects.isNull(rsaPublicKey)) {
            return null;
        }

        // RSA 公钥解密
        try {
            Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance(RSA_MODEL);
            cipher.init(Cipher.DECRYPT_MODE, rsaPublicKey);

            // 最大解密字节数，超出最大字节数需要分组解密
            byte[] inputBytes = Base64.getDecoder().decode(content);
            byte[] resultBytes = {};
            byte[] decBytes = {};
            int outputLength = inputBytes.length;
            int decOffset = 0;

            while (outputLength - decOffset > 0) {
                if (outputLength - decOffset > MAX_RSA_DEC_LEN) {
                    decBytes = cipher.doFinal(inputBytes, decOffset, MAX_RSA_DEC_LEN);
                    decOffset += MAX_RSA_DEC_LEN;
                } else {
                    decBytes = cipher.doFinal(inputBytes, decOffset, outputLength - decOffset);
                    decOffset = outputLength;
                }

                resultBytes = Arrays.copyOf(resultBytes, resultBytes.length + decBytes.length);
                System.arraycopy(decBytes, 0, resultBytes, resultBytes.length - decBytes.length, decBytes.length);
            }

            return new String(resultBytes);
        } catch (Exception e){
            e.printStackTrace();
        }

        return null;
    }

    public static String sendRequest(YajuleRequest yajuleRequest) {

        if (Objects.isNull(yajuleRequest)) {
            log.info("【{}】发送请求失败，参数不能为空",YajuleConsts.CHANNEL_NAME);
            return null;
        }

        Map<String,String> mapParams = new HashMap<>(6);
        mapParams.put("ver",yajuleRequest.getVer());
        mapParams.put("app_id",yajuleRequest.getAppId());
        mapParams.put("method",yajuleRequest.getMethod());
        mapParams.put("timestamp",yajuleRequest.getTimestamp().toString());
        mapParams.put("params",yajuleRequest.getParams());
        mapParams.put("sign",yajuleRequest.getSign());

        String response = "";
        for (int i=0;i<3;i++) {
            try {
                response = HttpUtil.httpsPost(YajuleConsts.API_URL, mapParams);
                if (StringUtil.isNotBlank(response)) {
                    break;
                }
            } catch (Exception exception) {
                log.info("【{}】发送请求失败,{}",YajuleConsts.CHANNEL_NAME,Objects.nonNull(exception)? exception.getMessage():"网络请求异常");
            }
        }

        if (StringUtil.isBlank(response)) {
            log.info("【{}】发送请求失败,请求参数：【{}】",YajuleConsts.CHANNEL_NAME,yajuleRequest.getParams());
            return null;
        }

        String encryptResponseData = "";
        String decryptResponseData = "";
        try {

            // 解析数据
            YajuleResponse yajuleResponse = JSON.parseObject(response,YajuleResponse.class);
            if (Objects.isNull(yajuleResponse)) {
                log.info("【{}】解析请求应答数据失败,应答数据：【{}】",YajuleConsts.CHANNEL_NAME,response);
                return null;
            }

            if (yajuleResponse.getCode() != YajuleConsts.CODE_SUCCESS) {
                log.info("【{}】应答返回失败,应答数据：【{}】",YajuleConsts.CHANNEL_NAME,response);
                return null;
            }

            JSONObject jsonObject = JSON.parseObject(yajuleResponse.getData());
            if (Objects.nonNull(jsonObject)) {
                encryptResponseData = jsonObject.getString("responseData");
            }

            if (StringUtil.isBlank(encryptResponseData)) {
                log.info("【{}】解析应答数据失败,应答数据：【{}】",YajuleConsts.CHANNEL_NAME,response);
                return null;
            }

            decryptResponseData = rsaDecrypt(encryptResponseData);
            if (StringUtil.isBlank(decryptResponseData)) {
                log.info("【{}】解码应答数据失败,应答数据：【{}】",YajuleConsts.CHANNEL_NAME,response);
                return null;
            }
        } catch (Exception e) {
            log.info("【{}】解析应答数据异常,应答数据：【{}】",YajuleConsts.CHANNEL_NAME,response);
            return null;
        }

        return decryptResponseData;
    }
}
